package com.beiding.render;

import com.beiding.render.RenderHelp.RenderResult;

import java.util.*;

//标准模板
public class TemplateImpl implements Template {

    //模板的文本
    private String text;

    //所有的占位符
    private List<Label> renders;

    //只解析最外层代码块
    //一块模板中可能蕴含着多个重复代码块

    //必须是有序的!!!!!
    private List<Label> blocks;

    //渲染出来的模板中应该有且仅有一个 root 标签
    private Label root;
    //模板  解析出来的 Block 区块  区块解析器  渲染解析器join

    //总是会有一个坐标
    private String coordinate;


    private RenderTime renderTime;

    public RenderTime getRenderTime() {
        return renderTime;
    }

    public void setRenderTime(RenderTime renderTime) {
        this.renderTime = renderTime;
    }

    /*

        声明至 渲染时

     */

 /*   TemplateFinder templateFinder;

    HandlerFinder handlerFinder;

    PluginFinder pluginFinder;

    URLFinder resourceFinder;*/
/*

    public void setResourceFinder(URLFinder resourceFinder) {
        this.resourceFinder = resourceFinder;
    }

    public URLFinder getResourceFinder() {
        return resourceFinder;
    }


    public TemplateFinder getTemplateFinder() {
        return templateFinder;
    }

    //构建自己的模板搜索器
    public void setTemplateFinder(TemplateFinder finder) {
        this.templateFinder = finder;
    }

    public HandlerFinder getHandlerFinder() {
        return handlerFinder;
    }

    public void setHandlerFinder(HandlerFinder handlerFinder) {
        this.handlerFinder = handlerFinder;
    }

    public PluginFinder getPluginFinder() {
        return pluginFinder;
    }

    public void setPluginFinder(PluginFinder pluginFinder) {
        this.pluginFinder = pluginFinder;
    }
*/


    public void setText(String text) {
        this.text = text;
    }

    public List<Label> getRenders() {
        return renders;
    }

    public void setRenders(List<Label> renders) {
        this.renders = renders;
    }

    public List<Label> getBlocks() {
        return blocks;
    }

    public void setBlocks(List<Label> blocks) {
        this.blocks = blocks;
    }

    public Label getRoot() {
        return root;
    }

    public void setRoot(Label root) {
        this.root = root;
    }

    public String getCoordinate() {
        return coordinate;
    }

    public void setCoordinate(String coordinate) {
        this.coordinate = coordinate;
    }

    public String render(Object d, BoxManager boxManager) {

        //被渲染的数据必须是Map,如果不是就转换为map
        Map od;
        if (d instanceof Map) {
            od = (Map) d;
        } else {
            od = MapUtils.objectToMap(d);
        }

        RenderContext rootContext = new RenderContext(null, null, null, boxManager) {
            @Override
            public Object get(Object key) {
                Object o = super.get(key);
                if (o != null) {
                    return o;
                }
                return od.get(key);
            }
        };

        rootContext.put("$b", boxManager);

        RenderHelp.preHandleCommands(root.commands);

        //根上下文
        This context = createRenderController(root, rootContext, boxManager, true);

        //放入引用的插件
        String plugins = root.commands.get("plugins");
        plugins = (String) ScriptUtils.parseHolder(context, plugins);
        if (plugins != null) {
            fillPluginBeans(context, plugins);
        }

        //在渲染之前执行一段初始化脚本,并将影响留在数据源中
        String initScript = root.commands.get("scriptBefore");
        initScript = (String) ScriptUtils.parseHolder(context, initScript);
        if (initScript != null) {
            ScriptUtils.exe(context, initScript);
        }

        String handlerBefore = root.commands.get("handlerBefore");
        handlerBefore = (String) ScriptUtils.parseHolder(context, handlerBefore);
        if (handlerBefore != null) {
            Handler handler = renderTime.getHandlerFinder().findHandler(this.coordinate, handlerBefore);
            if (handler == null) {
                throw new RuntimeException("未找到处理器:" + handlerBefore);
            }

        }

        //放入处理器
        putHandler(root, context);

        //处理缺省值
        putDefault(root, context);


        //可在处理前对数据进行预处理  TODO  不再预处理
        // data = dataBeforeRender(root, data, outCreator);


        //也可动态指定控制器
        String controller = root.commands.get("controller");
        controller = (String) ScriptUtils.parseHolder(context, controller);

        //使用控制器对根节点进行渲染操作
        String result = renderByController(context, controller);

        return text.replace(root.holder, result);
    }


    //本身就是一个上下文
    private static This createRenderController(Label bk, RenderContext parent, BoxManager outCreator, boolean isRoot) {
        return new This(parent, bk.template, bk.commands, outCreator, bk.attributes, bk.handlers, isRoot);
    }

    private Map<Label, Integer> holderOffsetMap = new HashMap<>();

    private List<Label> allLabels;

    //真实渲染操作
    String innerRender() {

        This _this = This.current();

        String handlerBefore = _this.commands.get("handlerBefore");
        handlerBefore = (String) ScriptUtils.parseHolder(_this, handlerBefore);
        if (handlerBefore != null) {
            Handler handler = renderTime.getHandlerFinder().findHandler(this.coordinate, handlerBefore);
            if (handler == null) {
                throw new RuntimeException("未找到处理器:" + handlerBefore);
            }
            handler.handle();//预处理handler
        }

        BoxManager outCreator = _this.outCreator;

        RenderResult rr = new RenderResult();

        if (allLabels == null) {
            allLabels = new ArrayList<>();
            allLabels.addAll(renders);
            allLabels.addAll(blocks);
            allLabels.sort(Comparator.comparingInt(a -> a.sort));
        }


        rr.text = text;

        RenderHelp.handleIndent(rr, _this);

        String swc = _this.getCommands().get("switch");


        Object switchObj = null;

        //TODO 判断是否是switch分支的情形
        if (swc != null) {
            //获取遍历的结果
            switchObj = ScriptUtils.exe(_this, swc);
        }


        //条件执行的结果
        Boolean conditionResult = null;

        boolean switchMatch = false;

        for (int idx = 0; idx < allLabels.size(); idx++) {


            Label label = allLabels.get(idx);

            RenderHelp.preHandleCommands(label.commands);

            if (switchMatch) {
                rr.text = rr.text.replace(label.holder, "");
                continue;
            }

            if (swc != null) {
                if (label.commands.containsKey("case")) {
                    //ignore
                } else if (label.commands.containsKey("default")) {
                    if (idx != allLabels.size() - 1) {
                        throw new RuntimeException("具备default的节点必须是最后一个节点");
                    }
                } else {
                    throw new RuntimeException("具备switch的节点内必须是具备case或default的节点");
                }
            }


            if (swc != null) {

                String aCase = label.commands.get("case");
                if (aCase != null) {
                    Object exe = ScriptUtils.exe(_this, aCase);

                    if (exe == null) {
                        if (switchObj == null) {
                            switchMatch = true;//TODO 执行后就停止
                        }
                    } else if (exe.equals(switchObj)) {
                        switchMatch = true;
                    }

                } else {
                    switchMatch = true;
                }

                if (!switchMatch) {
                    rr.text = rr.text.replace(label.holder, "");
                    continue;
                }

            }

            int indent = holderOffsetMap.computeIfAbsent(label, k -> ContentUtils.indentOfText(rr.text, label.holder));

            //TODO 如果需要隐藏掉当前行
            String removeLine = label.commands.get("removeLine");

            if (removeLine != null) {
                boolean a = "a".equals(removeLine) || "any".equals(removeLine);
                rr.text = ContentUtils.removeLine(rr.text, label.holder, a);
            }

            if (label.type.equals("render")) {

                String bh = _this.commands.get("handlerBefore");
                bh = (String) ScriptUtils.parseHolder(_this, bh);
                if (bh != null) {
                    Handler handler = renderTime.getHandlerFinder().findHandler(this.coordinate, bh);
                    if (handler == null) {
                        throw new RuntimeException("未找到处理器:" + bh);
                    }
                    handler.handle();//预处理handler
                }

                String initScript = label.commands.get("scriptBefore");
                initScript = (String) ScriptUtils.parseHolder(_this, initScript);
                if (initScript != null) {
                    ScriptUtils.exe(_this, initScript);
                }

                //放入handler的映射
                putHandler(label, _this);

                //处理缺省值
                putDefault(label, _this);


                //条件判断
                boolean b = false;

                if (label.commands.containsKey("if")) {
                    String condition = label.commands.get("if");
                    condition = (String) ScriptUtils.parseHolder(_this, condition);
                    conditionResult = ScriptUtils.exeBoolean(_this, condition);
                    if (conditionResult) {
                        b = true;
                    }
                } else if (label.commands.containsKey("elif")) {
                    if (conditionResult == null) {
                        throw new RuntimeException("具有elif指令的标签必须在具有if指令的标签后");
                    }
                    if (!conditionResult) {
                        String condition = label.commands.get("elif");
                        condition = (String) ScriptUtils.parseHolder(_this, condition);
                        conditionResult = ScriptUtils.exeBoolean(_this, condition);
                        if (conditionResult) {
                            b = true;
                        }
                    }
                } else if (label.commands.containsKey("else")) {
                    if (conditionResult == null) {
                        throw new RuntimeException("具有else指令的标签必须在具有if指令或elif指令的标签后");
                    }

                    if (!conditionResult) {
                        b = true;
                    }

                    conditionResult = null;
                } else {
                    b = true;
                    conditionResult = null;
                }


                if (b) {

                    //判断是否需要遍历
                    String repeat = label.commands.get("for");
                    repeat = (String) ScriptUtils.parseHolder(_this, repeat);

                    if (repeat != null) {
                        StringBuilder bodyBuilder = new StringBuilder();
                        ScriptUtils.doFor(_this, repeat, map -> {

                            String be = (String) ScriptUtils.parseHolder(map, label.body);

                            Object exe = ScriptUtils.exe(map, be);
                            if (exe == null) {
                                exe = "";
                            }

                            String body = exe.toString();
                            String as = label.commands.get("as");
                            as = (String) ScriptUtils.parseHolder(map, as);
                            if (as != null) {
                                handleAs(_this, as, body);
                            }
                            if (!label.commands.containsKey("hide")) {
                                bodyBuilder.append(body);
                            }
                        });

                        String t = bodyBuilder.toString();

                        //TODO 不应该计算偏移
                       // t = ContentUtils.indent(t, indent, true, 1);

                        String log = label.commands.get("log");
                        if (log != null) {
                            RenderConfig.getLogger().log(log, t);
                        }

                        rr.text = rr.text.replace(label.holder, t);
                    } else {//不可重复的
                        String be = (String) ScriptUtils.parseHolder(_this, label.body);
                        Object exe = ScriptUtils.exe(_this, be);
                        if (exe == null) {
                            exe = "";
                        }
                        String t = exe.toString();

                        //TODO 渲染结果应该原样输出,不应该在计算偏移
                        //t = ContentUtils.indent(t, indent, true, 1);
                        String log = label.commands.get("log");
                        if (log != null) {
                            RenderConfig.getLogger().log(log, t);
                        }
                        String as = label.commands.get("as");
                        as = (String) ScriptUtils.parseHolder(_this, as);
                        if (as != null) {
                            handleAs(_this, as, t);
                        }
                        if (!label.commands.containsKey("hide")) {
                            rr.text = rr.text.replace(label.holder, t);
                        } else {
                            rr.text = rr.text.replace(label.holder, "");
                        }
                    }

                } else {

                    rr.text = rr.text.replace(label.holder, "");
                }
            } else {

                final RenderContext blockContext = new RenderContext(_this, this, Collections.EMPTY_MAP, outCreator);

                //放入引用的插件
                String plugins = label.commands.get("plugins");

                plugins = (String) ScriptUtils.parseHolder(blockContext, plugins);
                if (plugins != null) {
                    fillPluginBeans(blockContext, plugins);
                }

                String beforeScript = label.commands.get("scriptBefore");
                beforeScript = (String) ScriptUtils.parseHolder(blockContext, beforeScript);



                //放入映射的处理器
                putHandler(label, blockContext);

                //条件判断
                boolean b = false;

                if (label.commands.containsKey("if")) {
                    String condition = label.commands.get("if");
                    condition = (String) ScriptUtils.parseHolder(_this, condition);
                    conditionResult = ScriptUtils.exeBoolean(_this, condition);
                    if (conditionResult) {
                        b = true;
                    }
                } else if (label.commands.containsKey("elif")) {
                    if (conditionResult == null) {
                        throw new RuntimeException("具有elif指令的标签必须在具有if指令的标签后");
                    }
                    if (!conditionResult) {
                        String condition = label.commands.get("elif");
                        condition = (String) ScriptUtils.parseHolder(_this, condition);
                        conditionResult = ScriptUtils.exeBoolean(_this, condition);
                        if (conditionResult) {
                            b = true;
                        }
                    }
                } else if (label.commands.containsKey("else")) {
                    if (conditionResult == null) {
                        throw new RuntimeException("具有else指令的标签必须在具有if指令或elif指令的标签后");
                    }

                    if (!conditionResult) {
                        b = true;
                    }

                    conditionResult = null;
                } else {
                    b = true;
                    conditionResult = null;
                }

                StringBuilder bodyBuilder = new StringBuilder();
                if (b) {

                    String repeat = label.commands.get("for");
                    repeat = (String) ScriptUtils.parseHolder(blockContext, repeat);

                    //递归遍历
                    if (repeat != null) {

                        String finalBeforeScript = beforeScript;

                      //  StringBuilder bd = new StringBuilder();

                        List<String> bodies = new ArrayList<>();

                        ScriptUtils.doFor(blockContext, repeat, map -> {

                            This forContext = createRenderController(label, _this, outCreator, false);

                            forContext.putAll(blockContext);

                            //TODO 将所有的变量放入???
                            forContext.putAll(map);


                            //处理缺省值
                            putDefault(label, forContext);

                            //渲染前对数据进行预处理
                            //  oMap = dataBeforeRender(block, oMap, outCreator);

                            //创建渲染控制器

                            //TODO 把影响留在子作用域中
                            if (finalBeforeScript != null) {
                                ScriptUtils.exe(forContext, finalBeforeScript);
                            }

                            //判断是否自定义了控制器
                            String controller = label.commands.get("controller");
                            controller = (String) ScriptUtils.parseHolder(forContext, controller);

                            //开始渲染
                            String body = renderByController(forContext, controller);

                            //在引用之前执行引用前处理逻辑
                            //   oMap = dataBeforeCall(block, oMap, outCreator);

                            //如果有引用就使用引用处理
                            RenderResult renderResult = callRender(forContext, label, body, outCreator);

                            body = renderResult.text;

                            //if (renderResult.change) {
                            //  System.out.println(body);
                          //  bd.append(body);

                            //TODO 添加进bodies中
                            bodies.add(body);


                            //body = ContentUtils.indent(body, indent, true, 1);
                            //}

                        });

                        //     System.out.println(bd);

                        //TODO 判断是否具有join指令
                        String join = label.commands.get("join");
                        join = (String) ScriptUtils.parseHolder(blockContext, join);

                        if (join == null) {
                            join = "";
                        }


                        bodyBuilder.append(ContentUtils.indent(TextUtils.join(bodies,join), indent, true, 1));

                    } else {

                        //处理缺省值

                        //渲染前对数据预处理
                        //    oData = dataBeforeRender(block, oData, outCreator);

                        String body;

                        //创建渲染控制器
                        This renderController = createRenderController(label, _this, outCreator, false);

                        renderController.putAll(blockContext);

                        putDefault(label, renderController);

                        if (beforeScript != null) {
                            ScriptUtils.exe(blockContext, beforeScript);
                        }


                        //判断是否自定义控制器
                        String controller = label.commands.get("controller");
                        controller = (String) ScriptUtils.parseHolder(blockContext, controller);

                        //渲染出体
                        body = renderByController(renderController, controller);

                        //处理引用前
                        //   oData = dataBeforeCall(block, oData, outCreator);

                        //引用模板渲染
                        RenderResult renderResult = callRender(blockContext, label, body, outCreator);

                        body = renderResult.text;

                        //TODO 这里的change的意义是什么
                        //if (renderResult.change) {
                        body = ContentUtils.indent(body, indent, true, 1);
                        //}

                        //获取偏移量

                        bodyBuilder.append(body);
                    }

                    rr.text = rr.text.replace(label.holder, bodyBuilder.toString());
                } else {
                    rr.text = rr.text.replace(label.holder, "");
                }

            }

        }


        return rr.text;

    }

    private void putDefault(Label label, Map data) {
        String s = label.commands.get("values");
        s = (String) ScriptUtils.parseHolder(data, s);
        if (s != null) {
            String json = "var json={" + s + "};json;";

            //如果解析失败将无法继续
            Map<String, Object> exe = (Map<String, Object>) ScriptUtils.exe(new HashMap(), json);

            exe.forEach((k, v) -> {

                if (data.get(k) == null) {
                    data.put(k, v);
                }

            });

        }
    }

    private void putHandler(Label block, RenderContext oData) {
        for (Map.Entry<String, String> nh : block.handlers.entrySet()) {
            String value = nh.getValue();
            value = (String) ScriptUtils.parseHolder(oData, value);
            Handler handler = renderTime.getHandlerFinder().findHandler(this.coordinate, value);
            if (handler != null) {
                ScriptHelper.statementHandler(oData, nh.getKey(), handler);
            } else {
                System.err.println("处理器未找到:" + value);
            }
        }
    }

    private String giBody(This _this, String body) {
        String s = _this.commands.get("gapHead");
        s = (String) ScriptUtils.parseHolder(_this, s);
        if (s != null) {
            int v = 0;
            try {
                v = Integer.valueOf(s);
            } catch (Exception ignore) {
            }
            body = ContentUtils.gapHead(body, v);
        }

        s = _this.commands.get("gapTail");
        s = (String) ScriptUtils.parseHolder(_this, s);
        if (s != null) {
            int v = 0;
            try {
                v = Integer.valueOf(s);
            } catch (Exception ignore) {
            }
            body = ContentUtils.gapTail(body, v);
        }

        String ofs = _this.getCommands().get("indent");
        ofs = (String) ScriptUtils.parseHolder(_this, ofs);
        if (ofs != null) {
            int indent = 0;
            try {
                indent = Integer.valueOf(ofs);
            } catch (Exception ignore) {
            }
            body = ContentUtils.indent(body, indent, false, 0);
        }
        return body;
    }

    private String renderByController(This _this, String controller) {

        String body;

        //如果自定义了控制器则采用自定义的控制器,否则采用以下逻辑直接渲染
        if (controller == null) {

            //使用渲染时渲染
            renderTime.render(_this);

            body = _this.getResult();

            body = giBody(_this, body);

            //输出重定向后本地依然输出
            String out = _this.getCommands().get("out");
            out = (String) ScriptUtils.parseHolder(_this, out);
            if (out != null) {
                Box renderControllerOut = _this.createOut(out);
                if (renderControllerOut != null) {
                    if (renderControllerOut.canWrite()) {
                        renderControllerOut.write(body);
                    }
                }
            }
        } else {

            //采用给定的控制器进行渲染
            Handler handler = renderTime.getHandlerFinder().findHandler(this.coordinate, controller);
            if (handler == null) {
                throw new RuntimeException("未找到控制器:" + controller);
            }

            //结果copy
            ScriptHelper.moveGlobal(_this);

            //使用指定的处理器进行处理
            //TODO
            This aThis = This.currentThisMember.get();
            try {
                This.currentThisMember.set(_this);
                handler.handle(_this);
            } finally {
                This.currentThisMember.set(aThis);
            }
            body = _this.getResult();
            if (body == null) {
                body = "";
            }
        }

        //TODO 不需要偏移内容
        String log = _this.getCommands().get("log");
        if (log != null) {
            log = (String) ScriptUtils.parseHolder(_this, log);
            RenderConfig.getLogger().log(log, body);
        }
        body = giBody(_this, body);

        String as = _this.commands.get("as");
        as = (String) ScriptUtils.parseHolder(_this, as);
        if (as != null) {
            //TODO 强转???
            handleAs(_this.parent, as, body);
        }

        /*if (_this.getCommands().containsKey("hide")) {
            return "";
        }*/


        return body;
    }

    private void handleAs(Map map, String key, String body) {
        ScriptHelper.set(map, key, body);
    }

    //支持插件,数据属性高于一切
    private void fillPluginBeans(Map data, String plugins) {
        Map<String, Object> pluginBeans = renderTime.getPluginFinder().findPluginBeans(coordinate, plugins);
        if (pluginBeans != null && pluginBeans.size() > 0) {
            pluginBeans.forEach((k, v) -> {
                if (k != null && v != null) {
                    if (data.get(k) != null) {
                        System.err.println("插件名称与已有属性重复,无法使用:" + k);
                    } else {
                        data.put(k, v);
                    }
                }
            });
        }
    }

 /*   private Map dataBeforeRender(Label block, Map map, OutCreator outCreator) {
        //使用给定的处理器对参数进行预处理
        String dataBeforeRender = block.commands.get("data-before-render");
        dataBeforeRender = (String) ScriptUtils.parseHolder(map, dataBeforeRender);
        Map r = handleData(map, dataBeforeRender);

        if (r == null) {
            r = new RenderContext(Collections.EMPTY_MAP, block.template, block.commands, outCreator);
        }

        return r;
    }*/

    //使用数据作为 this
/*
    private Map handleData(Map map, String dataBeforeRender) {
        if (dataBeforeRender != null) {
            Handler handler = renderTime.getHandlerFinder().findHandler(this.coordinate, dataBeforeRender);
            if (handler == null) {
                throw new RuntimeException("未找到参数预处理器:" + dataBeforeRender);
            }
            map = (Map) handler.handle(null, map);
        }
        return map;
    }
*/

    /*  private Map dataBeforeCall(Label block, Map map, OutCreator outCreator) {

          //使用给定的处理器处理引用模板传入的参数
          String dataBeforeCall = block.commands.get("data-before-call");
          dataBeforeCall = (String) ScriptUtils.parseHolder(map, dataBeforeCall);
          Map r = handleData(map, dataBeforeCall);
          if (r == null) {
              r = new RenderContext(Collections.EMPTY_MAP, block.template, block.commands, outCreator);
          }

          return r;
      }
  */


    //模板调用
    private RenderResult callRender(Map data, Label block, String body, BoxManager outCreator) {

        RenderResult renderResult = new RenderResult();

        //change的意义是什么
        //renderResult.change = false;
        renderResult.text = body;
        //调用内部模板时只能调用当前模板内的??
        String call = block.commands.get("call");
        call = (String) ScriptUtils.parseHolder(data, call);

        if (call != null) {
            Template template = renderTime.getTemplateFinder().findTemplate(this.coordinate, call);
            if (template == null) {
                throw new RuntimeException("未找到模板:" + call);
            }

            Map d;

            String _d = block.commands.get("data");

            if (_d != null) {
                Object o = ScriptUtils.parseHolder(data, _d);
                if (!(o instanceof Map)) {
                    d = new TransmissibleMap(MapUtils.objectToMap(o));
                } else {
                    d = new TransmissibleMap((Map) o);
                }
            } else {
                d = new HashMap();
            }


            d.put("__body__", body);

            //将自己body内渲染的结果以 _body 传递到子模板中使用
            block.attributes.forEach((k, v) -> {
                Object o = ScriptUtils.exe(data, v);
                d.put(k, o);
            });

            body = template.render(d, outCreator);
            renderResult.text = body;
            //renderResult.change = true;
        }
        return renderResult;
    }


    public String getText() {
        return text;
    }


}

